#pragma once

#include <AVUIEventableObject.h>

namespace AVUI {

typedef TDelegate<Object> DispatcherOperationCallback;

class DispatcherPriority
{
public:
    enum Enum
    {
        Invalid = -1,
        Inactive = 0,
        SystemIdle = 1,
        ApplicationIdle = 2,
        ContextIdle = 3,
        Background = 4,
        Input = 5,
        Loaded = 6,
        Render = 7,
        DataBind = 8,
        Normal = 9,
        Send = 10
    };
};

class DispatcherOperationStatus
{
public:
    enum Enum
    {
        Pending,
        Aborted,
        Completed,
        Executing
    };
};


class DispatcherOperation : public Object
{
public:
    DispatcherOperation(DispatcherPriority::Enum priority, DispatcherOperationCallback* pCallback, Object* pArg) :
                        m_priority(priority), m_pCallback(pCallback), m_pArg(pArg) { }

    DispatcherPriority::Enum get_Priority() { return m_priority; }
    DispatcherOperationCallback* get_Callback() { return m_pCallback; }
    Object* get_Argument() { return m_pArg; }
    DispatcherOperationStatus::Enum get_Status() { return m_status; }

    void Abort() { };
private:
    DispatcherPriority::Enum m_priority;
    TRefCountedPtr<DispatcherOperationCallback> m_pCallback;
    TRefCountedPtr<Object> m_pArg;
    DispatcherOperationStatus::Enum m_status;
};

class UIElement;
class InputManager;
class TimeManager;
class DispatcherTimer;
class DataBindEngine;
class ContextLayoutManager;
class Dispatcher : public EventableObject
{
    friend class Application;
    friend class FrameworkContextData;
public:
    DECLARE_ELEMENT(Dispatcher, Object);

    static Dispatcher* get_CurrentDispatcher() { return ms_pCurrentDispatcher; }

    Dispatcher();
    ~Dispatcher();

    InputManager* get_InputManager() { return m_pInputManager; }
    TimeManager* get_TimeManager() { return m_pTimeManager; }
    DataBindEngine* get_DataBindEngine() { return m_pDataBindEngine; }

    void AddTimer(DispatcherTimer* pTimer);
    void RemoveTimer(DispatcherTimer* pTimer);

    DispatcherOperation* BeginInvoke(DispatcherPriority::Enum priority, DispatcherOperationCallback* pCallback, Object* pArg = NULL);

    bool CheckAccess() { return true; }
    void VerifyAccess() { }

    ContextLayoutManager* get_ContextLayoutManager() { return m_pContextLayoutManager; }
    void set_ContextLayoutManager(ContextLayoutManager* pContextLayoutManager) { m_pContextLayoutManager = pContextLayoutManager; }

    IMPLEMENT_EVENT(ShutdownStarted, EventArgs);
    IMPLEMENT_EVENT(ShutdownFinished, EventArgs);

private:

    static void set_CurrentDispatcher(Dispatcher* pDispatcher) { ms_pCurrentDispatcher = pDispatcher; }
    Object* get_FrameworkContext() { return m_pFrameworkContext; }
    void set_FrameworkContext(Object* pFrameworkContext) { m_pFrameworkContext = pFrameworkContext; }

    void Update();
    void UpdateOperations(int minPriority, int maxPriority);

    ItemStructList< TRefCountedPtr<DispatcherOperation> > m_dispatcherOperations;

    InputManager* m_pInputManager;
    TimeManager* m_pTimeManager;
    DataBindEngine* m_pDataBindEngine;

    ItemStructList<TRefCountedPtr<DispatcherTimer> > m_dispatcherTimers;
    ItemStructList<TRefCountedPtr<DispatcherTimer> > m_tickedTimers;
    TRefCountedPtr<Object> m_pFrameworkContext;

    TRefCountedPtr<ContextLayoutManager> m_pContextLayoutManager;

    static AVUI_THREAD_LOCAL Dispatcher* ms_pCurrentDispatcher;
};

};
